package org.dreamwork.jasmine2.engine;

import org.dreamwork.jasmine2.ajax.RequestMethod;
import org.dreamwork.jasmine2.annotation.AjaxMethod;
import org.dreamwork.jasmine2.annotation.AjaxParameter;
import org.dreamwork.jasmine2.annotation.AjaxService;
import org.dreamwork.util.IConverter;
import org.dreamwork.util.StringUtil;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * Created by IntelliJ IDEA.
 * User: seth.yang
 * Date: 2010-12-9
 * Time: 23:48:02
 */
public class AjaxServlet extends HttpServlet {
    private static class AjaxServiceInfo {
        String methodName, serviceName;
    }

    @Override
    public void init () throws ServletException {
        try {
            AjaxManager.loadServiceList ();
        } catch (Exception e) {
            e.printStackTrace ();
            throw new ServletException (e);
        }
    }

    @Override
    @SuppressWarnings ("unchecked")
    protected void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String m = request.getMethod ();
        RequestMethod method = RequestMethod.parse (m);
        AjaxServiceInfo info = getName (request);
        if (StringUtil.isEmpty (info.serviceName)) {
            response.sendError (HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        Class c = AjaxManager.get (info.serviceName);

        AjaxService as = (AjaxService) c.getAnnotation (AjaxService.class);
        try {
            Object o = c.newInstance ();
            Method[] methods = c.getMethods ();
            for (Method am : methods) {
                if (am.getName ().equals (info.methodName)) {
                    AjaxMethod aam = am.getAnnotation (AjaxMethod.class);
                    if (aam == null) {
                        response.sendError (HttpServletResponse.SC_NOT_FOUND, "Service \"" + info.methodName + "\" not exists ");
                        return;
                    }
                    if (!aam.support ()) {
                        response.sendError (HttpServletResponse.SC_NOT_ACCEPTABLE, "Service \"" + info.methodName + "\" is not an AjaxService!");
                        return;
                    }
                    if (method != aam.method ()) {
                        response.sendError (HttpServletResponse.SC_METHOD_NOT_ALLOWED, "ONLY '" + method + "' IS ALLOWED!");
                        return;
                    }

                    execute (o, am, aam, request, response);
                    return;
                }
            }
            response.sendError (HttpServletResponse.SC_NOT_FOUND, "Service \"" + info.methodName + "\" not exists ");
        } catch (Exception e) {
            e.printStackTrace ();
            throw new ServletException (e);
        }
    }

    private AjaxServiceInfo getName (HttpServletRequest request) {
        AjaxServiceInfo info = new AjaxServiceInfo ();
        String path = request.getServletPath ();
        int pos = path.lastIndexOf ('/'), p2 = path.lastIndexOf ('.');
        info.methodName = path.substring (pos + 1, p2);
        path = path.substring (0, pos);
        pos = path.lastIndexOf ('/');
        info.serviceName = path.substring (pos + 1);
        return info;
    }

    @SuppressWarnings ("unchecked")
    private void execute (Object bean, Method method, AjaxMethod am, HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpContext context = new HttpContext ();
        context.setApplication (getServletContext ());
        context.setRequest (request);
        context.setResponse (response);
        context.setSession (request.getSession ());
        Annotation[][] anns = method.getParameterAnnotations ();
        Class[] types = method.getParameterTypes ();
        Object[] values = new Object[anns.length];
        for (int i = 0; i < anns.length; i ++) {
            AjaxParameter ap = (AjaxParameter) anns [i][0];
            String value = request.getParameter (ap.name ());
            Class<? extends IConverter> cc = ap.converter ();
            IConverter converter = cc.newInstance ();
            values [i] = converter.cast (types [i], value);
        }
        Object ret = method.invoke (bean, values);
        if (ret == null) return;

        String contentType = am.contentType ();
        Class<? extends IConverter> cc = am.converter ();
        IConverter c = cc.newInstance ();
        response.setContentType (contentType);
        if (contentType.startsWith ("text/")) {
            Writer writer = response.getWriter ();
            writer.write (c.cast (ret));
            writer.flush ();
        } else {
            OutputStream out = response.getOutputStream ();
            out.write (c.castToByteArray (ret));
            out.flush ();
        }
        response.flushBuffer ();
    }
}